/**
 * Flickr set
 * @param {Flickr.SetSummary|!Object} api Response from Flickr API call
 * @constructor
 */
function Set(api)
{
	"use strict";

	/** @type {Set} */
	var _this = this;
	/** @type {ImageCache} */
	var _cache = null;
	/** @type {String} */
	this.id = api.id;
	/** @type {Number} 0-based index of currently viewed photo in the set */
	this.photoIndex = 0;
	/** @type {Boolean} whether details have been loaded (irrespective of photo completion) */
	this.loaded = false;
	/** @type {String} */
	this.title = null;
	/** @type {String} */
	this.description = null;
	/** @type {String} */
	this.facebookID = null;
	/** @type {int} */
	this.total = 0;
	/** @type {google.maps.LatLngBounds} */
	this.boundary = null;
	/** @type {Array.<Photo>} */
	this.photos = [];
	/** @type {String} */
	this.url = '';

	function init()
	{
		parseTitle(api.title);
		parseDescription(api.description);
		api = null;
	}

	/**
	 * Create link from url and title
	 * @return {String}
	 */
	this.link = function()
	{
		return '<a href="/' + _this.url + '">' + _this.title + '</a>';
	};

	/**
	 * Shuffle the photos array
	 * @return {Set}
	 */
	this.shufflePhotos = function()
	{
		_this.photos = Format.shuffle(_this.photos);
		return _this;
	};

	/**
	 * Set photoIndex to match the given photo
	 * @param {Photo} photo
	 */
	this.makeCurrentPhoto = function(photo)
	{
		_this.photoIndex = _this.photos.indexOf(photo);
	};

	/**
	 * Set current photo index to the given photo ID
	 * @param {String} id Flickr photo ID
	 * @param {Boolean} [updateCache = false] Whether to update cache if photo is found
	 * @return {Boolean} Whether the photo ID was found
	 */
	this.updateIndex = function(id, updateCache)
	{
		for (var i = 0; i < _this.photos.length; i++)
		{
			if (_this.photos[i].id == id)
			{
				_this.photoIndex = parseInt(i);
				if (updateCache) { _cache.load(); }
				return true;
			}
		}
		return false;
	};

	/**
	 * Load set details from Flickr
	 * @param {Flickr} flickr
	 * @param {Array.<Enum.size>} imageSizes Sizes for which to retrieve URLs
	 * @param {function(Set)} [callback] Optional method to call when load completes
	 */
	this.load = function(flickr, imageSizes, callback)
	{
		if (_this.loaded)
		{
			if (callback) { callback(_this); }
		}
		else
		{
			flickr.getSet(_this.id, imageSizes, function(setPhotos, setInfo)
			{
				/** @type {Photo} */
				var p = null;
				_this.total = setPhotos.total;

				for (var i = 0; i < setPhotos.photo.length; i++)
				{
					p = new Photo(setPhotos.photo[i], imageSizes, p);
					_this.photos.push(p);
				}
				if (setInfo)
				{
					parseTitle(setInfo.title._content);
					parseDescription(setInfo.description._content);
				}

				_this.loaded = true;

				if (callback) { callback(_this); }
			},
				Format.isEmpty(_this.title)	// whether to also getInfo() for the photoset
			)
		}
	};

	/**
	 * Begin downloading set photos
	 * @param {String} [photoID] Optional ID of first photo to show
	 * @return {Set}
	 */
	this.beginPhotoDownload = function(photoID)
	{
		if (photoID !== undefined) { this.updateIndex(photoID); }
		_cache = new ImageCache(_this);
		_cache.load();
		return _this;
	};

	/**
	 * Load previous photo
	 * @return {Boolean} whether previous photo exists
	 */
	this.previousPhoto = function()
	{
		if (_this.photoIndex == 0) { return false; }

		_this.photoIndex--;
		_cache.load();
		return true;
	};

	/**
	 * Load next photo
	 * @return {Boolean} whether next photo exists
	 */
	this.nextPhoto = function()
	{
		if (_this.photoIndex + 1 >= _this.photos.length) { return false; }

		_this.photoIndex++;
		_cache.load();
		return true;
	};

	/**
	 * Photo at current index
	 * @return {Photo}
	 */
	this.currentPhoto = function() { return _this.photos[_this.photoIndex]; };

	/**
	 * Add set photos to map
	 * @param {google.maps.Map} map
	 * @param {String|google.maps.MarkerImage} mark
	 * @param {String|google.maps.MarkerImage} shadow
	 * @param {Function(PhotoObject)} clickHandler
	 * @return {Boolean}
	 */
	this.addToMap = function(map, mark, shadow, clickHandler)
	{
		if (_this.boundary == null)
		{
			var p;

			_this.boundary = new google.maps.LatLngBounds();

			for (var i = 0; i < _this.photos.length; i++)
			{
				p = _this.photos[i];
				p.addToMap(map, mark, shadow, clickHandler);

				if (p.mapMarker != null)
				{
					_this.boundary.extend(p.mapMarker.getPosition());
					p.mapMarker.setMap(map);
				}
			}
		}
		if (!_this.boundary.isEmpty())
		{
			map.fitBounds(_this.boundary);
			return true;
		}
		return false;
	};

	/**
	 * Remove every photo in set from the Google map
	 * @return {Set}
	 */
	this.removeFromMap = function()
	{
		for (var i = 0; i < _this.photos.length; i++) { _this.photos[i].removeFromMap(); }
		return _this;
	};

	/**
	 * Enable or disable photo preview (tooltip) on map
	 * @param {Boolean} [enable = true] whether to display on map
	 * @return {Set}
	 */
	this.previewOnMap = function(enable)
	{
		enable = (enable === undefined) ? true : enable;
		for (var i = 0; i < _this.photos.length; i++) { _this.photos[i].previewOnMap = enable; }
		return _this;
	};

	/**
	 * Format set title
	 * @param {String} text
	 */
	function parseTitle(text) { _this.title = Format.text(text); }

	/**
	 * Format set description
	 * @param {String} text
	 */
	function parseDescription(text)
	{
		/** @type {RegExp} */
		var re = Enum.pattern.facebookID;
		re.lastIndex = 0;

		if (text)
		{
			if (re.test(text))
			{
				re.lastIndex = 0;
				var matches = re.exec(text);
				_this.facebookID = matches[0];
				text = text.replace(_this.facebookID, '');
			}
			_this.description = Format.text(text);
		}
	}

	init();
}